package internal;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;

/**
 * 需要提供消息推送的对象,注册中心
 * Created by DJJ on 2019/3/28.
 */
public class MyRegistry {
    /**
     * 这里涉及,一个topic对应的多个订阅的推送方法,用队列来封装
     */
    private final ConcurrentMap<String, ConcurrentLinkedDeque<MySubscriber>> subscriberContainer=new ConcurrentHashMap<>();

    /**
     * 对外暴露的方法
     * @param topic
     * @return
     */
    public ConcurrentLinkedDeque<MySubscriber> scanSubscriber(final  String topic){
        return subscriberContainer.get(topic);
    }
    /**
     * 绑定
     * @param subscriber
     */
    public void bind(Object subscriber){
        List<Method> subscribeMethods = getSubscribeMethods(subscriber);
        subscribeMethods.stream().forEach(method -> tierSubscriber(subscriber,method));
    }

    /**
     * 解绑
     * @// TODO: 2019/3/28
     * @param subscriber
     */
    public void unbind(Object subscriber){
        subscriberContainer.forEach((k,queue)->
                queue.forEach(obj->{
                         if (obj==subscriber){
                             //在并发情况下删除比较麻烦,这里使用标记,使其不能使用
                             obj.setDisable(true);
                         } })
                );
    }

    /**
     * 获取对应要推送类下面的符合要求的推送方法
     * @param subscriber
     * @return
     */
    private List<Method> getSubscribeMethods(Object subscriber){
         final List<Method> methods=new ArrayList<>();
        Class<?> temp = subscriber.getClass();
        //获取对应父类的方法
        //获取对应的类下面符合要求需要推送的方法:1,贴了MySubscribe,参数为1个,并且为public方法
        while (temp!=null){
            Method[] declaredMethods = temp.getDeclaredMethods();
            Arrays.stream(declaredMethods).filter(c->c.isAnnotationPresent(MySubscribe.class) &&
                    c.getParameterCount()==1 && c.getModifiers()== Modifier.PUBLIC).forEach(methods::add);
            temp = temp.getSuperclass();
        }
        return methods;
    }

    /**
     * 按topic将要调用的方法存储到容器中
     * {@link MySubscribe}
     * @param subscriber 订阅类的对象
     * @param method MySubscribe标记的方法
     */
    private void tierSubscriber(Object subscriber,Method method){
        MySubscribe annotation = method.getAnnotation(MySubscribe.class);
        String topic = annotation.topic();
        //没有就创建一个ConcurrentLinkedDeque
        subscriberContainer.computeIfAbsent(topic,k->new ConcurrentLinkedDeque<>());
        subscriberContainer.get(topic).add(new MySubscriber(subscriber,method));
    }
}
